﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using AMM.Model;
using AMM.Model.Store;

namespace AMM.DataAccess.Store {
    public class InStoreOrderRepository : BaseRepository<InStoreOrder> {

        private int _pageSize;

        public InStoreOrderRepository(int pageSize) {
            this._pageSize = pageSize;
        }

        public InStoreOrder FetchInStoreOrderById(int inStoreOrderId) {
            using (var ctx = this.Context) {
                return ctx.InStoreOrders
                    .Include("InStoreOrderDetails")
                    .FirstOrDefault(x => x.InStoreOrderId == inStoreOrderId);
            }
        }

        public InStoreOrder FetchInStoreOrderByIdForView(int inStoreOrderId) {
            using (var ctx = this.Context) {
                return ctx.InStoreOrders
                    .Include("InStoreOrderDetails")
                    .Include("InStoreOrderDetails.Material")
                    .FirstOrDefault(x => x.InStoreOrderId == inStoreOrderId);
            }
        }

        public List<InStoreOrderDetail> FetchMaterialViewModelListByInStoreOrderId(int inStoreOrderId) {
            using (var ctx = this.Context) {
                return ctx.InStoreOrderDetails
                    .Include("Material")
                    .Include("Material.Basic")
                    .Where(x => x.InStoreOrderId == inStoreOrderId).ToList();
            }
        }

        public void InsertInStoreOrder(InStoreOrder inStoreOrder) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        // Add store amount
                        List<MaterialStore> existMSList =
                            ctx.MaterialStores.Where(x => x.StoreBasisId == inStoreOrder.StoreBasisId).ToList();
                        inStoreOrder.InStoreOrderDetails.ToList().ForEach(x => {
                            MaterialStore ms = existMSList.FirstOrDefault(y => y.MaterialId == x.MaterialId);
                            if (ms == null) {
                                ms = new MaterialStore() {
                                    MaterialStoreId = Guid.NewGuid().GetHashCode(),
                                    MaterialId = x.MaterialId,
                                    StoreBasisId = inStoreOrder.StoreBasisId,
                                    Quantity = x.Quantity,
                                    InStoreTotal = x.Quantity
                                };
                                ctx.MaterialStores.Attach(ms);
                                ctx.Entry(ms).State = EntityState.Added;
                            }
                            else {
                                ms.Quantity += x.Quantity;
                                ms.InStoreTotal += x.Quantity;
                                ctx.MaterialStores.Attach(ms);
                                ctx.Entry(ms).State = EntityState.Modified;
                            }
                        });

                        ctx.InStoreOrders.Add(inStoreOrder);
                        ctx.Entry(inStoreOrder).State = EntityState.Added;

                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public void UpdateInStoreOrder(InStoreOrder inStoreOrder) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        inStoreOrder.InStoreOrderDetails.Where(x => x.ClassStatus == ClassStatus.Added).ToList().ForEach(x => {
                            x.InStoreOrder = inStoreOrder;
                            x.InStoreOrderId = inStoreOrder.InStoreOrderId;
                            x.InStoreOrderDetailsId = Guid.NewGuid().GetHashCode();
                        });
                        // do not try to merge with above codes, because you can't change the fields which was marked as primary key
                        inStoreOrder.InStoreOrderDetails.ToList().ForEach(x => {
                            ctx.InStoreOrderDetails.Attach(x);
                            switch (x.ClassStatus) {
                                case ClassStatus.UnChanged:
                                    ctx.InStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Unchanged;
                                    break;
                                case ClassStatus.Updated:
                                    x.ModifiedBy = inStoreOrder.ModifiedBy;
                                    x.ModifiedDatetime = DateTime.Now;
                                    ctx.InStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Modified;
                                    break;
                                case ClassStatus.Added:
                                    x.CreatedBy = inStoreOrder.ModifiedBy;
                                    x.CreatedDatetime = DateTime.Now;
                                    ctx.InStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Added;
                                    break;
                                case ClassStatus.Deleted:
                                    ctx.InStoreOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Deleted;
                                    break;
                            }
                        });

                        ctx.InStoreOrders.Attach(inStoreOrder);
                        ctx.Entry(inStoreOrder).State = EntityState.Modified;

                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public void DeleteMultipleInStoreOrder(params int[] inStoreOrderIds) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        ctx.InStoreOrderDetails.Where(x => inStoreOrderIds.Contains(x.InStoreOrderId)).ToList().ForEach(x => {
                            ctx.InStoreOrderDetails.Remove(x);
                        });
                        ctx.InStoreOrders.Where(x => inStoreOrderIds.Contains(x.InStoreOrderId)).ToList().ForEach(x => {
                            ctx.InStoreOrders.Remove(x);
                        });
                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public List<InStoreOrder> FetchInStoreOrderList(InStoreOrderSearchModel searchModel, int pageIndex) {
            using (var ctx = this.Context) {
                var query = ctx.InStoreOrders
                    .Include("SuspenseOrder")
                    .Include("Basic")
                    .Include("StoreBasi")
                    .Include("InStoreOrderDetails")
                    .Include("InStoreOrderDetails.Material")
                    .Where(x => !string.IsNullOrEmpty(x.Code));

                if (!string.IsNullOrEmpty(searchModel.InStoreOrderCode)) {
                    query = query.Where(x => x.Code.Contains(searchModel.InStoreOrderCode));
                }
                if (!string.IsNullOrEmpty(searchModel.SuspenseOrderCode)) {
                    query = query.Where(x => x.SuspenseOrder.Code.Contains(searchModel.SuspenseOrderCode));
                }
                if (!string.IsNullOrEmpty(searchModel.MaterialCode)) {
                    query = query.Where(x => x.InStoreOrderDetails.Any(y => y.Material.Code.Contains(searchModel.MaterialCode)));
                }
                if (searchModel.InStoreDateStart.HasValue) {
                    query = query.Where(x => x.InStoreOrderDetails.Any(y => y.CreatedDatetime >= DbFunctions.TruncateTime(searchModel.InStoreDateStart.Value)));
                }
                if (searchModel.InStoreDateEnd.HasValue) {
                    query = query.Where(x => x.InStoreOrderDetails.Any(y => y.CreatedDatetime <= DbFunctions.TruncateTime(searchModel.InStoreDateEnd.Value)));
                }
                if (searchModel.MaterialInStoreTypeId.HasValue) {
                    query = query.Where(x => x.InStoreTypeId == searchModel.MaterialInStoreTypeId.Value);
                }
                if (searchModel.StoreBasisId.HasValue) {
                    query = query.Where(x => x.StoreBasisId == searchModel.StoreBasisId.Value);
                }

                searchModel.Records = query.Count();
                pageIndex = (pageIndex - 1) * this._pageSize > searchModel.Records ? 1 : pageIndex;

                return query.OrderByDescending(x => x.CreatedDatetime).Skip((pageIndex - 1) * this._pageSize).Take(this._pageSize).ToList();
            }
        }

        public int GetTheMaxFlowNumber(string startString) {
            using (var ctx = this.Context) {
                InStoreOrder inStoreOrder = ctx.InStoreOrders
                    .Where(x => x.Code.StartsWith(startString))
                    .OrderByDescending(x => x.Code)
                    .FirstOrDefault();
                int maxFlowNumber = inStoreOrder == null
                    ? 0
                    : Convert.ToInt32(inStoreOrder.Code.Substring(inStoreOrder.Code.Length - 2, 2));
                return Math.Max(ctx.InStoreOrders.Count(x => x.Code.StartsWith(startString)), maxFlowNumber);
            }
        }

        public List<InStoreOrderDetail> FetchInStoreOrderDetailList(int suspenseOrderId) {
            using (var ctx = this.Context) {
                var query = from a in ctx.SuspenseOrders.Where(x => x.SuspenseOrderId == suspenseOrderId)
                            join b in ctx.InStoreOrders on a.SuspenseOrderId equals b.SuspenseOrderId
                            join c in ctx.InStoreOrderDetails on b.InStoreOrderId equals c.InStoreOrderId
                            select c;

                return query.ToList();
            }
        }

    }
}
